Skip to content

MT6589 DRM and Blade panels (v6.16)#33

Closed
akku1139 wants to merge 90 commits into
blade/v6.16from
dev/v6.16/mt6589-drm
Closed

MT6589 DRM and Blade panels (v6.16)#33
akku1139 wants to merge 90 commits into
blade/v6.16from
dev/v6.16/mt6589-drm

Conversation

@akku1139

@akku1139 akku1139 commented Mar 14, 2026

Copy link
Copy Markdown
Member

akku1139 and others added 6 commits January 2, 2026 22:50
The mt65xx SoC family uses an earlier version of the SMI, which we refer
to as gen 0. Unlike gen 1 or gen 2, gen 0 requires simultaneous access to
two distinct MMIO ranges: an AO base for IOMMU configuration and ext base
for OSTD, FIFO, and bandwidth limiter setup.

Remove the union in struct mtk_smi to allow simultaneous use of
smi_ao_base and base. Refactor the probe function into a switch statement
to handle the different generation requirements cleanly.

Signed-off-by: rva3 <rva333@protonmail.com>
The mt65xx SoC family utilizes a larb design that incorporates features
from both gen 1 and gen 2 architectures.

Introduce a specific configuration callback for this generation, which
implements the port security configuration typical for gen 1 while
including the ostd settings found in gen 2. Additionally, add a bandwidth
calibration flag for some of the gen 0 SoCs.

Signed-off-by: rva3 <rva333@protonmail.com>
The mt65xx SoC family utilizes an earlier version of the IOMMU which
shares the same v1 architecture but has minor hardware differences
compared to mt2701.

Introduce mtk_iommu_type to distinguish between mt2701 and mt65xx
variants and apply the necessary logic.

Signed-off-by: rva3 <rva333@protonmail.com>
@akku1139

akku1139 commented Mar 14, 2026

Copy link
Copy Markdown
Member Author

rva3 and others added 16 commits March 14, 2026 14:16
XXX: maybe we should drop this?..
Signed-off-by: rva3 <rva333@protonmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Add a header for the IOMMU ports on the MediaTek MT6589 SoC.

Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Add platform data for the SMI common on the MT6589 SoC.

Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Make sure to drop the reference taken when looking up the SMI device
during common probe on late probe failure (e.g. probe deferral) and on
driver unbind.

Fixes: 4740475 ("memory: mtk-smi: Add device link for smi-sub-common")
Fixes: 038ae37 ("memory: mtk-smi: add missing put_device() call in mtk_smi_device_link_common")
Cc: stable@vger.kernel.org	# 5.16: 038ae37
Cc: stable@vger.kernel.org	# 5.16
Cc: Yong Wu <yong.wu@mediatek.com>
Cc: Miaoqian Lin <linmq006@gmail.com>
Signed-off-by: Johan Hovold <johan@kernel.org>
Link: https://patch.msgid.link/20251121164624.13685-2-johan@kernel.org
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
Make sure to drop the reference taken when looking up the SMI device
during larb probe on late probe failure (e.g. probe deferral) and on
driver unbind.

Fixes: cc8bbe1 ("memory: mediatek: Add SMI driver")
Fixes: 038ae37 ("memory: mtk-smi: add missing put_device() call in mtk_smi_device_link_common")
Cc: stable@vger.kernel.org	# 4.6: 038ae37
Cc: stable@vger.kernel.org	# 4.6
Cc: Yong Wu <yong.wu@mediatek.com>
Cc: Miaoqian Lin <linmq006@gmail.com>
Signed-off-by: Johan Hovold <johan@kernel.org>
Link: https://patch.msgid.link/20251121164624.13685-3-johan@kernel.org
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
Clean up device link creation by bailing out early in case the SMI
platform device lookup fails.

Signed-off-by: Johan Hovold <johan@kernel.org>
Link: https://patch.msgid.link/20251121164624.13685-4-johan@kernel.org
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

akku1139 commented Mar 14, 2026

Copy link
Copy Markdown
Member Author

TODO:

mmsys (done by claude)
mt6572-mainline@7278659

mutex (done)
mt6572-mainline@a0b799a

ovl (done)
mt6572-mainline@39086e4

dsi (done)
mt6572-mainline@af8df08

drm main (done with mt2701_mmsys_driver_data)
mt6572-mainline@4fe7dec

In MT6589, this IP is called "dispsys" for some reason.
I don't know more than that.

Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
It's in
Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.yaml
now.

Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
everyone will say "fixme"

Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

Copy link
Copy Markdown
Member Author

mmsys routing: ddp_path.c

Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
akku1139 added 2 commits June 22, 2026 18:57
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

Copy link
Copy Markdown
Member Author

1369 page:
Smi_larb0
Smi_larb1
Smi_larb2
Smi_larb3
Smi_larb4
Audio
G3D

@akku1139

akku1139 commented Jun 22, 2026

Copy link
Copy Markdown
Member Author

drivers/memory/mtk-smi.c .port_in_larb = { 0, 10, 17, 29, 43, 53, 54, 54 },

drop too many ports

static unsigned int smi_port0_in_larbx[SMI_LARB_NR+1] = {0, 10, 17, 29, 44 ,56};
static unsigned int m4u_port0_in_larbx[SMI_LARB_NR+1] = {0, 10, 17, 29, 43 ,53};

needs fix

@akku1139

Copy link
Copy Markdown
Member Author

1380: SMI_L1ARB6

Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

akku1139 commented Jun 22, 2026

Copy link
Copy Markdown
Member Author

Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

Copy link
Copy Markdown
Member Author

akku1139 added 2 commits June 22, 2026 23:52
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

Copy link
Copy Markdown
Member Author

@akku1139

Copy link
Copy Markdown
Member Author
static unsigned int m4u_index_of_larb[SMI_LARB_NR] = {0,0,1,0,1};
static unsigned int m4u_index_of_larb5 = 0,  m4u_index_of_larb6 = 0;
static unsigned int smi_port0_in_larbx[SMI_LARB_NR+1] = {0, 10, 17, 29, 44 ,56};
static unsigned int m4u_port0_in_larbx[SMI_LARB_NR+1] = {0, 10, 17, 29, 43 ,53};
static unsigned int m4u_port_size_limit[M4U_PORT_NR] = {};

https://github.com/bq/aquaris-5/blob/bc1d0d0e35c969670e2d869001be944ad0c9c5e5/mediatek/platform/mt6589/kernel/drivers/m4u/m4u.c#L278-L282

@akku1139

akku1139 commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

@akku1139

akku1139 commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

@akku1139 akku1139 changed the base branch from blade/v6.16 to blade/v7.1 July 3, 2026 18:21
@akku1139

akku1139 commented Jul 3, 2026

Copy link
Copy Markdown
Member Author
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 3e359f6aede0..59645d3ef7c9 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -7,6 +7,7 @@
  *
  * Based on driver/iommu/mtk_iommu.c
  */
+#include <linux/array_size.h>
 #include <linux/bug.h>
 #include <linux/clk.h>
 #include <linux/component.h>
@@ -29,6 +30,7 @@
 #include <asm/barrier.h>
 #include <dt-bindings/memory/mtk-memory-port.h>
 #include <dt-bindings/memory/mt2701-larb-port.h>
+#include <dt-bindings/memory/mt6589-larb-port.h>
 #include <soc/mediatek/smi.h>
 
 #if defined(CONFIG_ARM)
@@ -51,6 +53,7 @@ struct dma_iommu_mapping {
 #define F_MMU_FAULT_VA_MSK			0xfffff000
 #define MTK_PROTECT_PA_ALIGN			128
 
+/* -------- Common M4U v1 register definitions (MT2701 and MT6589 core) -------- */
 #define REG_MMU_CTRL_REG			0x210
 #define F_MMU_CTRL_COHERENT_EN			BIT(8)
 #define REG_MMU_IVRP_PADDR			0x214
@@ -71,6 +74,8 @@ struct dma_iommu_mapping {
 #define REG_MMU_FAULT_VA			0x228
 #define REG_MMU_INVLD_PA			0x22C
 #define REG_MMU_INT_ID				0x388
+
+/* MT2701 specific (core space) */
 #define REG_MMU_INVALIDATE			0x5c0
 #define REG_MMU_INVLD_START_A			0x5c4
 #define REG_MMU_INVLD_END_A			0x5c8
@@ -81,14 +86,80 @@ struct dma_iommu_mapping {
 #define REG_MMU_DCM				0x5f0
 #define F_MMU_DCM_ON				BIT(1)
 #define REG_MMU_CPE_DONE			0x60c
+
+/* MT6589 global space registers */
+#define REG_MMUg_CTRL				0x00
+#define F_MMUg_CTRL_INV_EN0			BIT(0)
+#define F_MMUg_CTRL_INV_EN1			BIT(1)
+#define F_MMUg_CTRL_INV_EN2			BIT(2)	/* L2 */
+#define F_MMUg_CTRL_PRE_LOCK(en)		((en) ? BIT(3) : 0)
+#define F_MMUg_CTRL_PRE_EN			BIT(4)
+
+#define REG_MMUg_INVLD				0x04
+#define F_MMUg_INV_ALL				0x2
+#define F_MMUg_INV_RANGE			0x1
+
+#define REG_MMUg_INVLD_SA			0x08
+#define REG_MMUg_INVLD_EA			0x0C
+#define REG_MMUg_PT_BASE			0x10
+#define F_MMUg_PT_VA_MSK			0xffff0000
+
+#define REG_MMUg_L2_SEL				0x18
+#define F_MMUg_L2_SEL_FLUSH_EN(en)		((en) ? BIT(3) : 0)
+#define F_MMUg_L2_SEL_L2_ULTRA(en)		((en) ? BIT(2) : 0)
+#define F_MMUg_L2_SEL_L2_SHARE(en)		((en) ? BIT(1) : 0)
+#define F_MMUg_L2_SEL_L2_BUS_SEL(go_emi)	((go_emi) ? BIT(0) : 0)
+
+#define REG_MMUg_DCM				0x1C
+#define F_MMUg_DCM_ON(on)			((on) ? BIT(0) : 0)
+
+/* L2 cache registers (MT6589) */
+#define REG_L2_GDC_STATE			0x00
+#define F_L2_GDC_ST_EVENT_MSK			GENMASK(7,6)
+#define F_L2_GDC_ST_EVENT_VAL(val)		(((val) & 0x3) << 6)
+
+#define REG_L2_GDC_OP				0x04
+#define F_L2_GDC_BYPASS(en)			((en) ? BIT(10) : 0)
+#define F_L2_GDC_PERF_MASK(msk)			(((msk) & 0x7) << 7)
+#define GDC_PERF_MASK_HIT_MISS			0
+#define F_L2_GDC_LOCK_ALERT_DIS(dis)		((dis) ? BIT(6) : 0)
+#define F_L2_GDC_PERF_EN(en)			((en) ? BIT(5) : 0)
+#define F_L2_GDC_LOCK_TH(th)			(((th) & 0x3) << 2)
+#define F_L2_GDC_PAUSE_OP(op)			((op) & 0x3)
+#define GDC_NO_PAUSE				0
+
+#define REG_L2_GPE_STATUS			0x18
+#define F_L2_GPE_ST_RANGE_INV_DONE		BIT(1)
+#define F_L2_GPE_ST_PREFETCH_DONE		BIT(0)
+
+/* MT6589 core PFH distance / direction registers */
+#define REG_MMU_PFH_DIST(port)			(0x80 + (((port) >> 3) << 2))
+#define F_MMU_PFH_DIST_VAL(port, val)		(((val) & 0xf) << (((port) & 0x7) << 2))
+#define F_MMU_PFH_DIST_MASK(port)		F_MMU_PFH_DIST_VAL(port, 0xf)
+
+#define REG_MMU_PFH_DIR(port)			(((port) < 32) ? 0xF0 : 0xF4)
+#define F_MMU_PFH_DIR(port, val)		((!!(val)) << ((port) & 0x1f))
+
+/* SMI common registers (temporary, to be moved to SMI driver) */
+#define SMI_COMMON_EXT_BASE			0xf0202000
+#define SMI_COMMON_AO_BASE			0xf000E000
+#define REG_SMI_L1LEN				0x200
+#define F_SMI_L1LEN_AXROUTE_G3D_EMI(en)		((en) ? BIT(2) : 0)
+#define F_SMI_L1LEN_AXROUTE_AUDIO_EMI(en)	((en) ? BIT(5) : 0)
+#define REG_SMI_BUS_SEL				0x220
+#define F_SMI_BUS_SEL_larb0(mmu)		(((mmu) & 0x3) << 0)
+#define F_SMI_BUS_SEL_larb1(mmu)		(((mmu) & 0x3) << 2)
+#define F_SMI_BUS_SEL_larb2(mmu)		(((mmu) & 0x3) << 4)
+#define F_SMI_BUS_SEL_larb3(mmu)		(((mmu) & 0x3) << 6)
+#define F_SMI_BUS_SEL_larb4(mmu)		(((mmu) & 0x3) << 8)
+#define REG_SMI_SECUR_CON(x)			(0x05C0 + ((x) << 2))
+
+/* Common page table descriptor bits */
 #define F_DESC_VALID				0x2
 #define F_DESC_NONSEC				BIT(3)
-#define MT2701_M4U_TF_LARB(TF)			(6 - (((TF) >> 13) & 0x7))
-#define MT2701_M4U_TF_PORT(TF)			(((TF) >> 8) & 0xF)
 /* MTK generation one iommu HW only support 4K size mapping */
 #define MT2701_IOMMU_PAGE_SHIFT			12
 #define MT2701_IOMMU_PAGE_SIZE			(1UL << MT2701_IOMMU_PAGE_SHIFT)
-#define MT2701_LARB_NR_MAX			3
 
 /*
  * MTK m4u support 4GB iova address space, and only support 4K page
@@ -96,26 +167,74 @@ struct dma_iommu_mapping {
  */
 #define M2701_IOMMU_PGT_SIZE			SZ_4M
 
+#define MAX_M4U_CORES				2
+
+struct mtk_iommu_v1_data;
+
+struct mtk_iommu_v1_soc_data {
+	const char *compatible;
+	unsigned int num_cores;
+	bool has_global_base;
+	bool has_l2_cache;
+
+	void (*tlb_flush_all)(struct mtk_iommu_v1_data *data);
+	void (*tlb_flush_range)(struct mtk_iommu_v1_data *data,
+				unsigned long iova, size_t size);
+
+	void (*get_fault_larb_port)(u32 int_id, unsigned int *larb,
+				    unsigned int *port);
+
+	int (*hw_init)(struct mtk_iommu_v1_data *data);
+
+	u32 pt_base_reg_offset;
+	bool pt_base_in_global;
+
+	const int *larb_port_offsets;
+	unsigned int num_larb;
+};
+
+struct mtk_iommu_v1_core {
+	void __iomem *base;
+	int irq;
+	struct mtk_iommu_v1_data *data;
+	unsigned int id;
+};
+
 struct mtk_iommu_v1_suspend_reg {
+	/* MT2701 fields */
 	u32			standard_axi_mode;
 	u32			dcm_dis;
 	u32			ctrl_reg;
 	u32			int_control0;
+
+	/* MT6589 additional fields */
+	u32			mmug_ctrl;
+	u32			mmug_pt_base;
+	u32			mmug_l2_sel;
+	u32			mmug_dcm;
+	u32			l2_gdc_op;
+	u32			smi_l1len;
+	u32			smi_bus_sel;
+	u32			smi_secur_con[7];
 };
 
 struct mtk_iommu_v1_data {
-	void __iomem			*base;
-	int				irq;
-	struct device			*dev;
-	struct clk			*bclk;
-	phys_addr_t			protect_base; /* protect memory base */
-	struct mtk_iommu_v1_domain	*m4u_dom;
-
-	struct iommu_device		iommu;
-	struct dma_iommu_mapping	*mapping;
-	struct mtk_smi_larb_iommu	larb_imu[MTK_LARB_NR_MAX];
-
-	struct mtk_iommu_v1_suspend_reg	reg;
+	const struct mtk_iommu_v1_soc_data *soc;
+	struct device *dev;
+
+	struct mtk_iommu_v1_core cores[MAX_M4U_CORES];
+	void __iomem *global_base;
+	void __iomem *l2_base;
+
+	struct clk *bclk;
+	phys_addr_t protect_base;
+	struct mtk_iommu_v1_domain *m4u_dom;
+
+	struct iommu_device iommu;
+	struct dma_iommu_mapping *mapping;
+	struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX];
+
+	struct mtk_iommu_v1_suspend_reg reg;
 };
 
 struct mtk_iommu_v1_domain {
@@ -150,73 +269,143 @@ static const int mt2701_m4u_in_larb[] = {
 	MT2701_LARB2_PORT_OFFSET, MT2701_LARB3_PORT_OFFSET
 };
 
-static inline int mt2701_m4u_to_larb(int id)
+static const int mt6589_m4u_in_larb[] = {
+	MT6589_LARB0_PORT_OFFSET, MT6589_LARB1_PORT_OFFSET,
+	MT6589_LARB2_PORT_OFFSET, MT6589_LARB3_PORT_OFFSET,
+	MT6589_LARB4_PORT_OFFSET, MT6589_LARB5_PORT_OFFSET
+};
+
+static inline int mtk_iommu_v1_to_larb(struct mtk_iommu_v1_data *data, int id)
 {
+	const int *offsets = data->soc->larb_port_offsets;
+	int num = data->soc->num_larb;
 	int i;
 
-	for (i = ARRAY_SIZE(mt2701_m4u_in_larb) - 1; i >= 0; i--)
-		if ((id) >= mt2701_m4u_in_larb[i])
+	for (i = num - 1; i >= 0; i--)
+		if (id >= offsets[i])
 			return i;
 
 	return 0;
 }
 
-static inline int mt2701_m4u_to_port(int id)
+static inline int mtk_iommu_v1_to_port(struct mtk_iommu_v1_data *data, int id)
 {
-	int larb = mt2701_m4u_to_larb(id);
+	int larb = mtk_iommu_v1_to_larb(data, id);
+	return id - data->soc->larb_port_offsets[larb];
+}
 
-	return id - mt2701_m4u_in_larb[larb];
+static inline void m4u_set_field(void __iomem *base, u32 reg, u32 mask, u32 val)
+{
+	u32 regval = readl_relaxed(base + reg);
+	regval = (regval & ~mask) | val;
+	writel_relaxed(regval, base + reg);
 }
 
-static void mtk_iommu_v1_tlb_flush_all(struct mtk_iommu_v1_data *data)
+/* MT2701 (single core, no global space) */
+static void mt2701_tlb_flush_all(struct mtk_iommu_v1_data *data)
 {
-	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
-			data->base + REG_MMU_INV_SEL);
-	writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE);
-	wmb(); /* Make sure the tlb flush all done */
+	void __iomem *base = data->cores[0].base;
+	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, base + REG_MMU_INV_SEL);
+	writel_relaxed(F_ALL_INVLD, base + REG_MMU_INVALIDATE);
+	wmb();
 }
 
-static void mtk_iommu_v1_tlb_flush_range(struct mtk_iommu_v1_data *data,
-					 unsigned long iova, size_t size)
+static void mt2701_tlb_flush_range(struct mtk_iommu_v1_data *data,
+				   unsigned long iova, size_t size)
 {
-	int ret;
+	void __iomem *base = data->cores[0].base;
 	u32 tmp;
+	int ret;
 
-	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
-		data->base + REG_MMU_INV_SEL);
-	writel_relaxed(iova & F_MMU_FAULT_VA_MSK,
-		data->base + REG_MMU_INVLD_START_A);
+	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, base + REG_MMU_INV_SEL);
+	writel_relaxed(iova & F_MMU_FAULT_VA_MSK, base + REG_MMU_INVLD_START_A);
 	writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK,
-		data->base + REG_MMU_INVLD_END_A);
-	writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE);
+		   base + REG_MMU_INVLD_END_A);
+	writel_relaxed(F_MMU_INV_RANGE, base + REG_MMU_INVALIDATE);
 
-	ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE,
-				tmp, tmp != 0, 10, 100000);
+	ret = readl_poll_timeout_atomic(base + REG_MMU_CPE_DONE,
+					tmp, tmp != 0, 10, 100000);
 	if (ret) {
 		dev_warn(data->dev,
 			 "Partial TLB flush timed out, falling back to full flush\n");
-		mtk_iommu_v1_tlb_flush_all(data);
+		mt2701_tlb_flush_all(data);
 	}
-	/* Clear the CPE status */
-	writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
+	writel_relaxed(0, base + REG_MMU_CPE_DONE);
+}
+
+/* MT6589 (global control, L2) */
+static void mt6589_tlb_flush_all(struct mtk_iommu_v1_data *data)
+{
+	u32 reg = F_MMUg_CTRL_INV_EN0 | F_MMUg_CTRL_INV_EN1;
+	if (data->l2_base)
+		reg |= F_MMUg_CTRL_INV_EN2;
+
+	writel_relaxed(reg, data->global_base + REG_MMUg_CTRL);
+	writel_relaxed(F_MMUg_INV_ALL, data->global_base + REG_MMUg_INVLD);
+
+	if (data->l2_base) {
+		u32 event;
+		readl_poll_timeout_atomic(data->l2_base + REG_L2_GDC_STATE,
+					 event,
+					 event & F_L2_GDC_ST_EVENT_MSK,
+					 10, 100000);
+		writel_relaxed(0, data->l2_base + REG_L2_GDC_STATE);
+	}
+}
+
+static void mt6589_tlb_flush_range(struct mtk_iommu_v1_data *data,
+				   unsigned long iova, size_t size)
+{
+	u32 reg = F_MMUg_CTRL_INV_EN0 | F_MMUg_CTRL_INV_EN1;
+	if (data->l2_base)
+		reg |= F_MMUg_CTRL_INV_EN2;
+
+	writel_relaxed(reg, data->global_base + REG_MMUg_CTRL);
+	writel_relaxed(iova & F_MMU_FAULT_VA_MSK,
+		   data->global_base + REG_MMUg_INVLD_SA);
+	writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK,
+		   data->global_base + REG_MMUg_INVLD_EA);
+	writel_relaxed(F_MMUg_INV_RANGE, data->global_base + REG_MMUg_INVLD);
+
+	if (data->l2_base) {
+		u32 status;
+		readl_poll_timeout_atomic(data->l2_base + REG_L2_GPE_STATUS,
+					  status,
+					  status & F_L2_GPE_ST_RANGE_INV_DONE,
+					  10, 100000);
+		writel_relaxed(0, data->l2_base + REG_L2_GPE_STATUS);
+	}
+}
+
+static void mt2701_get_fault_larb_port(u32 int_id, unsigned int *larb,
+				       unsigned int *port)
+{
+	*larb = 6 - ((int_id >> 13) & 0x7);
+	*port = (int_id >> 8) & 0xF;
+}
+
+static void mt6589_get_fault_larb_port(u32 int_id, unsigned int *larb,
+				       unsigned int *port)
+{
+	*larb = (int_id >> 12) & 0x7;
+	*port = (int_id >> 8) & 0xF;
 }
 
 static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 {
-	struct mtk_iommu_v1_data *data = dev_id;
+	struct mtk_iommu_v1_core *core = dev_id;
+	struct mtk_iommu_v1_data *data = core->data;
 	struct mtk_iommu_v1_domain *dom = data->m4u_dom;
 	u32 int_state, regval, fault_iova, fault_pa;
 	unsigned int fault_larb, fault_port;
 
 	/* Read error information from registers */
-	int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST);
-	fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA);
+	int_state = readl_relaxed(core->base + REG_MMU_FAULT_ST);
+	fault_iova = readl_relaxed(core->base + REG_MMU_FAULT_VA) & F_MMU_FAULT_VA_MSK;
+	fault_pa = readl_relaxed(core->base + REG_MMU_INVLD_PA);
+	regval = readl_relaxed(core->base + REG_MMU_INT_ID);
 
-	fault_iova &= F_MMU_FAULT_VA_MSK;
-	fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA);
-	regval = readl_relaxed(data->base + REG_MMU_INT_ID);
-	fault_larb = MT2701_M4U_TF_LARB(regval);
-	fault_port = MT2701_M4U_TF_PORT(regval);
+	data->soc->get_fault_larb_port(regval, &fault_larb, &fault_port);
 
 	/*
 	 * MTK v1 iommu HW could not determine whether the fault is read or
@@ -225,16 +414,16 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 	if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
 			IOMMU_FAULT_READ))
 		dev_err_ratelimited(data->dev,
-			"fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d\n",
+			"fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d core=%d\n",
 			int_state, fault_iova, fault_pa,
-			fault_larb, fault_port);
+			fault_larb, fault_port, core->id);
 
 	/* Interrupt clear */
-	regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL);
+	regval = readl_relaxed(core->base + REG_MMU_INT_CONTROL);
 	regval |= F_INT_CLR_BIT;
-	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL);
+	writel_relaxed(regval, core->base + REG_MMU_INT_CONTROL);
 
-	mtk_iommu_v1_tlb_flush_all(data);
+	data->soc->tlb_flush_all(data);
 
 	return IRQ_HANDLED;
 }
@@ -242,14 +431,14 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data,
 				struct device *dev, bool enable)
 {
-	struct mtk_smi_larb_iommu    *larb_mmu;
-	unsigned int                 larbid, portid;
+	struct mtk_smi_larb_iommu *larb_mmu;
 	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	int i;
+	unsigned int larbid, portid, i, mmu_id;
+	void __iomem *base;
 
 	for (i = 0; i < fwspec->num_ids; ++i) {
-		larbid = mt2701_m4u_to_larb(fwspec->ids[i]);
-		portid = mt2701_m4u_to_port(fwspec->ids[i]);
+		larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]);
+		portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]);
 		larb_mmu = &data->larb_imu[larbid];
 
 		dev_dbg(dev, "%s iommu port: %d\n",
@@ -260,6 +449,26 @@ static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data,
 		else
 			larb_mmu->mmu &= ~MTK_SMI_MMU_EN(portid);
 	}
+
+	/* MT6589 specific: set default prefetch distance & direction */
+	if (data->soc->has_global_base) {
+		for (i = 0; i < fwspec->num_ids; i++) {
+			portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]);
+			larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]);
+
+			/* Determine which M4U core this LARB is connected to */
+			mmu_id = (larbid == 0 || larbid == 2) ? 0 : 1;
+			base = data->cores[mmu_id].base;
+
+			/* Set distance = 1 */
+			m4u_set_field(base, REG_MMU_PFH_DIST(portid),
+				      F_MMU_PFH_DIST_MASK(portid),
+				      F_MMU_PFH_DIST_VAL(portid, 1));
+			/* Set direction = 0 */
+			m4u_set_field(base, REG_MMU_PFH_DIR(portid),
+				      1 << (portid & 0x1f), 0);
+		}
+	}
 }
 
 static int mtk_iommu_v1_domain_finalise(struct mtk_iommu_v1_data *data)
@@ -273,7 +482,10 @@ static int mtk_iommu_v1_domain_finalise(struct mtk_iommu_v1_data *data)
 	if (!dom->pgt_va)
 		return -ENOMEM;
 
-	writel(dom->pgt_pa, data->base + REG_MMU_PT_BASE_ADDR);
+	if (data->soc->pt_base_in_global)
+		writel(dom->pgt_pa, data->global_base + data->soc->pt_base_reg_offset);
+	else
+		writel(dom->pgt_pa, data->cores[0].base + data->soc->pt_base_reg_offset);
 
 	dom->data = data;
 
@@ -365,7 +577,7 @@ static int mtk_iommu_v1_map(struct iommu_domain *domain, unsigned long iova,
 	spin_unlock_irqrestore(&dom->pgtlock, flags);
 
 	*mapped = i * MT2701_IOMMU_PAGE_SIZE;
-	mtk_iommu_v1_tlb_flush_range(dom->data, iova, *mapped);
+	dom->data->soc->tlb_flush_range(dom->data, iova, *mapped);
 
 	return i == pgcount ? 0 : -EEXIST;
 }
@@ -383,7 +595,7 @@ static size_t mtk_iommu_v1_unmap(struct iommu_domain *domain, unsigned long iova
 	memset(pgt_base_iova, 0, pgcount * sizeof(u32));
 	spin_unlock_irqrestore(&dom->pgtlock, flags);
 
-	mtk_iommu_v1_tlb_flush_range(dom->data, iova, size);
+	dom->data->soc->tlb_flush_range(dom->data, iova, size);
 
 	return size;
 }
@@ -433,6 +645,7 @@ static int mtk_iommu_v1_create_mapping(struct device *dev,
 			return -EINVAL;
 
 		dev_iommu_priv_set(dev, platform_get_drvdata(m4updev));
+		put_device(&m4updev->dev);
 	}
 
 	ret = iommu_fwspec_add_ids(dev, args->args, 1);
@@ -482,12 +695,12 @@ static struct iommu_device *mtk_iommu_v1_probe_device(struct device *dev)
 	data = dev_iommu_priv_get(dev);
 
 	/* Link the consumer device with the smi-larb device(supplier) */
-	larbid = mt2701_m4u_to_larb(fwspec->ids[0]);
-	if (larbid >= MT2701_LARB_NR_MAX)
+	larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[0]);
+	if (larbid >= MTK_LARB_NR_MAX)
 		return ERR_PTR(-EINVAL);
 
 	for (idx = 1; idx < fwspec->num_ids; idx++) {
-		larbidx = mt2701_m4u_to_larb(fwspec->ids[idx]);
+		larbidx = mtk_iommu_v1_to_larb(data, fwspec->ids[idx]);
 		if (larbid != larbidx) {
 			dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n",
 				larbid, larbidx);
@@ -509,14 +722,10 @@ static struct iommu_device *mtk_iommu_v1_probe_device(struct device *dev)
 
 static void mtk_iommu_v1_probe_finalize(struct device *dev)
 {
-	struct dma_iommu_mapping *mtk_mapping;
-	struct mtk_iommu_v1_data *data;
+	struct mtk_iommu_v1_data *data = dev_iommu_priv_get(dev);
 	int err;
 
-	data        = dev_iommu_priv_get(dev);
-	mtk_mapping = data->mapping;
-
-	err = arm_iommu_attach_device(dev, mtk_mapping);
+	err = arm_iommu_attach_device(dev, data->mapping);
 	if (err)
 		dev_err(dev, "Can't create IOMMU mapping - DMA-OPS will not work\n");
 }
@@ -524,17 +733,17 @@ static void mtk_iommu_v1_probe_finalize(struct device *dev)
 static void mtk_iommu_v1_release_device(struct device *dev)
 {
 	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	struct mtk_iommu_v1_data *data;
+	struct mtk_iommu_v1_data *data = dev_iommu_priv_get(dev);
 	struct device *larbdev;
 	unsigned int larbid;
 
 	data = dev_iommu_priv_get(dev);
-	larbid = mt2701_m4u_to_larb(fwspec->ids[0]);
+	larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[0]);
 	larbdev = data->larb_imu[larbid].dev;
 	device_link_remove(dev, larbdev);
 }
 
-static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
+static int mt2701_hw_init(struct mtk_iommu_v1_data *data)
 {
 	u32 regval;
 	int ret;
@@ -546,7 +755,7 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
 	}
 
 	regval = F_MMU_CTRL_COHERENT_EN | F_MMU_TF_PROTECT_SEL(2);
-	writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
+	writel_relaxed(regval, data->cores[0].base + REG_MMU_CTRL_REG);
 
 	regval = F_INT_TRANSLATION_FAULT |
 		F_INT_MAIN_MULTI_HIT_FAULT |
@@ -556,23 +765,104 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
 		F_INT_TLB_MISS_FAULT |
 		F_INT_PFH_DMA_FIFO_OVERFLOW |
 		F_INT_MISS_DMA_FIFO_OVERFLOW;
-	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL);
+	writel_relaxed(regval, data->cores[0].base + REG_MMU_INT_CONTROL);
 
-	/* protect memory,hw will write here while translation fault */
-	writel_relaxed(data->protect_base,
-			data->base + REG_MMU_IVRP_PADDR);
+	writel_relaxed(data->protect_base, data->cores[0].base + REG_MMU_IVRP_PADDR);
+	writel_relaxed(F_MMU_DCM_ON, data->cores[0].base + REG_MMU_DCM);
 
-	writel_relaxed(F_MMU_DCM_ON, data->base + REG_MMU_DCM);
+	return 0;
+}
 
-	if (devm_request_irq(data->dev, data->irq, mtk_iommu_v1_isr, 0,
-			     dev_name(data->dev), (void *)data)) {
-		writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR);
-		clk_disable_unprepare(data->bclk);
-		dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq);
-		return -ENODEV;
+static int mt6589_hw_init(struct mtk_iommu_v1_data *data)
+{
+	u32 regval;
+	int i, ret;
+	void __iomem *smi_ext, *smi_ao;
+
+	/* Map SMI common regions temporarily (should be done by SMI driver) */
+	smi_ext = ioremap(SMI_COMMON_EXT_BASE, 0x500);
+	smi_ao = ioremap(SMI_COMMON_AO_BASE, 0x1000);
+	if (!smi_ext || !smi_ao) {
+		dev_err(data->dev, "Failed to map SMI common registers\n");
+		ret = -ENOMEM;
+		goto err_iomap;
+	}
+
+	ret = clk_prepare_enable(data->bclk);
+	if (ret) {
+		dev_err(data->dev, "Failed to enable bclk\n");
+		goto err_clk;
+	}
+
+	/* ---- SMI routing (LARB to M4U core) ---- */
+	/* LARB 0 -> M4U0, 1 -> M4U1, 2 -> M4U0, 3 -> M4U1, 4 -> M4U1 */
+	regval = F_SMI_BUS_SEL_larb0(0) | F_SMI_BUS_SEL_larb1(1) |
+		 F_SMI_BUS_SEL_larb2(0) | F_SMI_BUS_SEL_larb3(1) |
+		 F_SMI_BUS_SEL_larb4(1);
+	writel_relaxed(regval, smi_ext + REG_SMI_BUS_SEL);
+
+	/* Default security: non-secure, domain=3, physical */
+	for (i = 0; i < 7; i++)
+		writel_relaxed(0x66666666, smi_ao + REG_SMI_SECUR_CON(i));
+
+	/* Route G3D and AUDIO to EMI */
+	m4u_set_field(smi_ext, REG_SMI_L1LEN,
+		      F_SMI_L1LEN_AXROUTE_G3D_EMI(1),
+		      F_SMI_L1LEN_AXROUTE_G3D_EMI(1));
+	m4u_set_field(smi_ext, REG_SMI_L1LEN,
+		      F_SMI_L1LEN_AXROUTE_AUDIO_EMI(1),
+		      F_SMI_L1LEN_AXROUTE_AUDIO_EMI(1));
+
+	/* ---- Global registers ---- */
+	writel_relaxed(F_MMUg_L2_SEL_FLUSH_EN(1) | F_MMUg_L2_SEL_L2_ULTRA(1) |
+		   F_MMUg_L2_SEL_L2_SHARE(0) | F_MMUg_L2_SEL_L2_BUS_SEL(1),
+		   data->global_base + REG_MMUg_L2_SEL);
+	writel_relaxed(F_MMUg_DCM_ON(1), data->global_base + REG_MMUg_DCM);
+
+	/* ---- L2 cache ---- */
+	if (data->l2_base) {
+		regval = F_L2_GDC_BYPASS(0) |
+			 F_L2_GDC_PERF_MASK(GDC_PERF_MASK_HIT_MISS) |
+			 F_L2_GDC_LOCK_ALERT_DIS(0) |
+			 F_L2_GDC_LOCK_TH(3) |
+			 F_L2_GDC_PAUSE_OP(GDC_NO_PAUSE);
+		writel_relaxed(regval, data->l2_base + REG_L2_GDC_OP);
 	}
 
+	/* ---- Per-core setup ---- */
+	for (i = 0; i < data->soc->num_cores; i++) {
+		void __iomem *base = data->cores[i].base;
+
+		regval = 0;  /* PFH enabled, walk enabled, cohere disabled */
+		regval |= F_MMU_TF_PROTECT_SEL(2);
+		writel_relaxed(regval, base + REG_MMU_CTRL_REG);
+
+		regval = F_INT_TRANSLATION_FAULT |
+			 F_INT_MAIN_MULTI_HIT_FAULT |
+			 F_INT_INVALID_PA_FAULT |
+			 F_INT_ENTRY_REPLACEMENT_FAULT |
+			 F_INT_TABLE_WALK_FAULT |
+			 F_INT_TLB_MISS_FAULT |
+			 F_INT_PFH_DMA_FIFO_OVERFLOW |
+			 F_INT_MISS_DMA_FIFO_OVERFLOW;
+		writel_relaxed(regval, base + REG_MMU_INT_CONTROL);
+
+		writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	}
+
+	/* Release temporary mappings */
+	iounmap(smi_ext);
+	iounmap(smi_ao);
+
 	return 0;
+
+err_clk:
+	iounmap(smi_ext);
+	iounmap(smi_ao);
+err_iomap:
+	if (smi_ext) iounmap(smi_ext);
+	if (smi_ao) iounmap(smi_ao);
+	return ret;
 }
 
 static const struct iommu_ops mtk_iommu_v1_ops = {
@@ -593,8 +883,39 @@ static const struct iommu_ops mtk_iommu_v1_ops = {
 	}
 };
 
+static const struct mtk_iommu_v1_soc_data mt2701_soc_data = {
+	.compatible = "mediatek,mt2701-m4u",
+	.num_cores = 1,
+	.has_global_base = false,
+	.has_l2_cache = false,
+	.tlb_flush_all = mt2701_tlb_flush_all,
+	.tlb_flush_range = mt2701_tlb_flush_range,
+	.get_fault_larb_port = mt2701_get_fault_larb_port,
+	.hw_init = mt2701_hw_init,
+	.pt_base_reg_offset = REG_MMU_PT_BASE_ADDR,
+	.pt_base_in_global = false,
+	.larb_port_offsets = mt2701_m4u_in_larb,
+	.num_larb = ARRAY_SIZE(mt2701_m4u_in_larb),
+};
+
+static const struct mtk_iommu_v1_soc_data mt6589_soc_data = {
+	.compatible = "mediatek,mt6589-m4u",
+	.num_cores = 2,
+	.has_global_base = true,
+	.has_l2_cache = true,
+	.tlb_flush_all = mt6589_tlb_flush_all,
+	.tlb_flush_range = mt6589_tlb_flush_range,
+	.get_fault_larb_port = mt6589_get_fault_larb_port,
+	.hw_init = mt6589_hw_init,
+	.pt_base_reg_offset = REG_MMUg_PT_BASE,
+	.pt_base_in_global = true,
+	.larb_port_offsets = mt6589_m4u_in_larb,
+	.num_larb = ARRAY_SIZE(mt6589_m4u_in_larb),
+};
+
 static const struct of_device_id mtk_iommu_v1_of_ids[] = {
-	{ .compatible = "mediatek,mt2701-m4u", },
+	{ .compatible = "mediatek,mt2701-m4u", .data = &mt2701_soc_data },
+	{ .compatible = "mediatek,mt6589-m4u", .data = &mt6589_soc_data },
 	{}
 };
 MODULE_DEVICE_TABLE(of, mtk_iommu_v1_of_ids);
@@ -606,52 +927,103 @@ static const struct component_master_ops mtk_iommu_v1_com_ops = {
 
 static int mtk_iommu_v1_probe(struct platform_device *pdev)
 {
-	struct device			*dev = &pdev->dev;
-	struct mtk_iommu_v1_data	*data;
-	struct resource			*res;
-	struct component_match		*match = NULL;
-	void				*protect;
-	int				larb_nr, ret, i;
+	struct device *dev = &pdev->dev;
+	struct mtk_iommu_v1_data *data;
+	const struct mtk_iommu_v1_soc_data *soc;
+	struct component_match *match = NULL;
+	void *protect;
+	int i, larb_nr, ret;
 
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
 
 	data->dev = dev;
+	soc = of_device_get_match_data(dev);
+	data->soc = soc;
 
-	/* Protect memory. HW will access here while translation fault.*/
+	/* Protect memory (HW writes here on translation fault) */
 	protect = devm_kcalloc(dev, 2, MTK_PROTECT_PA_ALIGN,
 			       GFP_KERNEL | GFP_DMA);
 	if (!protect)
 		return -ENOMEM;
 	data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN);
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	data->base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(data->base))
-		return PTR_ERR(data->base);
-
-	data->irq = platform_get_irq(pdev, 0);
-	if (data->irq < 0)
-		return data->irq;
+	/* Map register bases */
+	if (soc->has_global_base) {
+		data->global_base = devm_platform_ioremap_resource_byname(pdev,
+									  "global");
+		if (IS_ERR(data->global_base))
+			return PTR_ERR(data->global_base);
+	}
+	for (i = 0; i < soc->num_cores; i++) {
+		char name[8];
+		snprintf(name, sizeof(name), "m4u%d", i);
+		data->cores[i].base = devm_platform_ioremap_resource_byname(pdev,
+									    name);
+		if (IS_ERR(data->cores[i].base))
+			return PTR_ERR(data->cores[i].base);
+		data->cores[i].data = data;
+		data->cores[i].id = i;
+	}
+	if (soc->has_l2_cache) {
+		data->l2_base = devm_platform_ioremap_resource_byname(pdev,
+								      "l2cache");
+		if (IS_ERR(data->l2_base))
+			return PTR_ERR(data->l2_base);
+	}
 
+	/* Clock */
 	data->bclk = devm_clk_get(dev, "bclk");
 	if (IS_ERR(data->bclk))
 		return PTR_ERR(data->bclk);
 
+	/* Interrupts - request after hw_init to avoid spurious IRQs? but
+	   we need the core IRQs ready before registering ISR. We'll request
+	   them after hw_init for simplicity. */
+	ret = soc->hw_init(data);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < soc->num_cores; i++) {
+		struct mtk_iommu_v1_core *core = &data->cores[i];
+		char irqname[8];
+		snprintf(irqname, sizeof(irqname), "m4u%d", i);
+		core->irq = platform_get_irq_byname(pdev, irqname);
+		if (core->irq < 0) {
+			ret = core->irq;
+			goto err_clk;
+		}
+		ret = devm_request_irq(dev, core->irq, mtk_iommu_v1_isr, 0,
+				       dev_name(dev), core);
+		if (ret) {
+			dev_err(dev, "Failed to request IRQ %d for core%d\n",
+				core->irq, i);
+			goto err_clk;
+		}
+	}
+
+	/* LARB links */
 	larb_nr = of_count_phandle_with_args(dev->of_node,
 					     "mediatek,larbs", NULL);
-	if (larb_nr < 0)
-		return larb_nr;
+	if (larb_nr < 0) {
+		ret = larb_nr;
+		goto err_clk;
+	}
+	if (larb_nr > MTK_LARB_NR_MAX) {
+		ret = -EINVAL;
+		goto err_clk;
+	}
 
 	for (i = 0; i < larb_nr; i++) {
 		struct device_node *larbnode;
 		struct platform_device *plarbdev;
 
 		larbnode = of_parse_phandle(dev->of_node, "mediatek,larbs", i);
-		if (!larbnode)
-			return -EINVAL;
-
+		if (!larbnode) {
+			ret = -EINVAL;
+			goto out_put_larbs;
+		}
 		if (!of_device_is_available(larbnode)) {
 			of_node_put(larbnode);
 			continue;
@@ -660,11 +1032,14 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 		plarbdev = of_find_device_by_node(larbnode);
 		if (!plarbdev) {
 			of_node_put(larbnode);
-			return -ENODEV;
+			ret = -ENODEV;
+			goto out_put_larbs;
 		}
 		if (!plarbdev->dev.driver) {
 			of_node_put(larbnode);
-			return -EPROBE_DEFER;
+			put_device(&plarbdev->dev);
+			ret = -EPROBE_DEFER;
+			goto out_put_larbs;
 		}
 		data->larb_imu[i].dev = &plarbdev->dev;
 
@@ -674,29 +1049,29 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, data);
 
-	ret = mtk_iommu_v1_hw_init(data);
+	ret = iommu_device_sysfs_add(&data->iommu, dev, NULL,
+				     dev_name(dev));
 	if (ret)
-		return ret;
-
-	ret = iommu_device_sysfs_add(&data->iommu, &pdev->dev, NULL,
-				     dev_name(&pdev->dev));
-	if (ret)
-		goto out_clk_unprepare;
+		goto out_put_larbs;
 
 	ret = iommu_device_register(&data->iommu, &mtk_iommu_v1_ops, dev);
 	if (ret)
 		goto out_sysfs_remove;
 
-	ret = component_master_add_with_match(dev, &mtk_iommu_v1_com_ops, match);
+	ret = component_master_add_with_match(dev, &mtk_iommu_v1_com_ops,
+					      match);
 	if (ret)
 		goto out_dev_unreg;
-	return ret;
+	return 0;
 
 out_dev_unreg:
 	iommu_device_unregister(&data->iommu);
 out_sysfs_remove:
 	iommu_device_sysfs_remove(&data->iommu);
-out_clk_unprepare:
+out_put_larbs:
+	for (i = 0; i < MTK_LARB_NR_MAX; i++)
+		put_device(data->larb_imu[i].dev);
+err_clk:
 	clk_disable_unprepare(data->bclk);
 	return ret;
 }
@@ -704,26 +1079,56 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 static void mtk_iommu_v1_remove(struct platform_device *pdev)
 {
 	struct mtk_iommu_v1_data *data = platform_get_drvdata(pdev);
+	int i;
 
 	iommu_device_sysfs_remove(&data->iommu);
 	iommu_device_unregister(&data->iommu);
 
 	clk_disable_unprepare(data->bclk);
-	devm_free_irq(&pdev->dev, data->irq, data);
+	for (i = 0; i < data->soc->num_cores; i++)
+		devm_free_irq(&pdev->dev, data->cores[i].irq, &data->cores[i]);
 	component_master_del(&pdev->dev, &mtk_iommu_v1_com_ops);
+
+	for (i = 0; i < MTK_LARB_NR_MAX; i++)
+		put_device(data->larb_imu[i].dev);
 }
 
 static int __maybe_unused mtk_iommu_v1_suspend(struct device *dev)
 {
 	struct mtk_iommu_v1_data *data = dev_get_drvdata(dev);
 	struct mtk_iommu_v1_suspend_reg *reg = &data->reg;
-	void __iomem *base = data->base;
+	void __iomem *base = data->cores[0].base;
 
-	reg->standard_axi_mode = readl_relaxed(base +
-					       REG_MMU_STANDARD_AXI_MODE);
-	reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM);
+	/* Common core registers */
 	reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG);
 	reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL);
+
+	if (data->soc->has_global_base) {
+		reg->mmug_ctrl = readl_relaxed(data->global_base + REG_MMUg_CTRL);
+		reg->mmug_pt_base = readl_relaxed(data->global_base + REG_MMUg_PT_BASE);
+		reg->mmug_l2_sel = readl_relaxed(data->global_base + REG_MMUg_L2_SEL);
+		reg->mmug_dcm = readl_relaxed(data->global_base + REG_MMUg_DCM);
+		if (data->l2_base)
+			reg->l2_gdc_op = readl_relaxed(data->l2_base + REG_L2_GDC_OP);
+
+		/* Save SMI registers (temporary) */
+		void __iomem *smi_ext = ioremap(SMI_COMMON_EXT_BASE, 0x500);
+		void __iomem *smi_ao = ioremap(SMI_COMMON_AO_BASE, 0x1000);
+		if (smi_ext && smi_ao) {
+			reg->smi_l1len = readl_relaxed(smi_ext + REG_SMI_L1LEN);
+			reg->smi_bus_sel = readl_relaxed(smi_ext + REG_SMI_BUS_SEL);
+			for (int i = 0; i < 7; i++)
+				reg->smi_secur_con[i] = readl_relaxed(smi_ao + REG_SMI_SECUR_CON(i));
+		}
+		iounmap(smi_ext);
+		iounmap(smi_ao);
+	} else {
+		/* MT2701 specific */
+		base = data->cores[0].base;
+		reg->standard_axi_mode = readl_relaxed(base + REG_MMU_STANDARD_AXI_MODE);
+		reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM);
+	}
+
 	return 0;
 }
 
@@ -731,15 +1136,51 @@ static int __maybe_unused mtk_iommu_v1_resume(struct device *dev)
 {
 	struct mtk_iommu_v1_data *data = dev_get_drvdata(dev);
 	struct mtk_iommu_v1_suspend_reg *reg = &data->reg;
-	void __iomem *base = data->base;
-
-	writel_relaxed(data->m4u_dom->pgt_pa, base + REG_MMU_PT_BASE_ADDR);
-	writel_relaxed(reg->standard_axi_mode,
-		       base + REG_MMU_STANDARD_AXI_MODE);
-	writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM);
-	writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
-	writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL);
-	writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	void __iomem *base = data->cores[0].base;
+
+	if (data->soc->has_global_base) {
+		writel_relaxed(reg->mmug_ctrl, data->global_base + REG_MMUg_CTRL);
+		writel_relaxed(reg->mmug_pt_base, data->global_base + REG_MMUg_PT_BASE);
+		writel_relaxed(reg->mmug_l2_sel, data->global_base + REG_MMUg_L2_SEL);
+		writel_relaxed(reg->mmug_dcm, data->global_base + REG_MMUg_DCM);
+		if (data->l2_base)
+			writel_relaxed(reg->l2_gdc_op, data->l2_base + REG_L2_GDC_OP);
+
+		/* Restore SMI registers */
+		void __iomem *smi_ext = ioremap(SMI_COMMON_EXT_BASE, 0x500);
+		void __iomem *smi_ao = ioremap(SMI_COMMON_AO_BASE, 0x1000);
+		if (smi_ext && smi_ao) {
+			writel_relaxed(reg->smi_l1len, smi_ext + REG_SMI_L1LEN);
+			writel_relaxed(reg->smi_bus_sel, smi_ext + REG_SMI_BUS_SEL);
+			for (int i = 0; i < 7; i++)
+				writel_relaxed(reg->smi_secur_con[i], smi_ao + REG_SMI_SECUR_CON(i));
+		}
+		iounmap(smi_ext);
+		iounmap(smi_ao);
+	}
+
+	/* Per-core restore (common) */
+	for (int i = 0; i < data->soc->num_cores; i++) {
+		base = data->cores[i].base;
+		writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
+		writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL);
+		writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	}
+
+	if (!data->soc->has_global_base) {
+		/* MT2701 extra */
+		base = data->cores[0].base;
+		writel_relaxed(reg->standard_axi_mode, base + REG_MMU_STANDARD_AXI_MODE);
+		writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM);
+	}
+
+	if (data->m4u_dom && data->m4u_dom->pgt_pa) {
+		if (data->soc->pt_base_in_global)
+			writel_relaxed(data->m4u_dom->pgt_pa, data->global_base + data->soc->pt_base_reg_offset);
+		else
+			writel_relaxed(data->m4u_dom->pgt_pa, data->cores[0].base + data->soc->pt_base_reg_offset);
+	}
+
 	return 0;
 }
 

wip deepseek

https://chat.deepseek.com/a/chat/s/57f1d733-d562-41af-a8b4-8e6da9896b80
https://chat.deepseek.com/a/chat/s/6c605754-6ef8-4067-998c-ec63ea13855a

@akku1139 akku1139 changed the base branch from blade/v7.1 to blade/v6.16 July 3, 2026 18:25
@akku1139

akku1139 commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

do not merge into blade/6.16

@akku1139 akku1139 changed the title MT6589 DRM and Blade panels MT6589 DRM and Blade panels (v6.16) Jul 4, 2026
@akku1139 akku1139 closed this Jul 4, 2026
@akku1139 akku1139 deleted the dev/v6.16/mt6589-drm branch July 4, 2026 09:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants